//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 *  Copyright (C) Extenex Corporation 2001
 *  Copyright (C) Compaq Computer Corporation, 1998, 1999
 *  Copyright (C) Intrinsyc, Inc., 2002
 *
 *  PXA USB controller driver - Endpoint zero management
 *
 *  Please see:
 *    linux/Documentation/arm/SA1100/SA1100_USB 
 *  for more info.
 *
 *  02-May-2002
 *   Frank Becker (Intrinsyc) - derived from sa1100 usb_ctl.c
 * 
 */

#if 0
#ifdef HAVE_CONFIG_H
# include <blob/config.h>
#endif

#include <blob/arch.h>
#include <blob/types.h>
#include <blob/serial.h>

#else

#include "config.h"
#include "util.h"
#include "bulverde.h"
#include "tat.h"

#endif

#include "linux/errno.h"
#include "pxa_usb.h"  /* public interface */
#include "bvd_usb_ctl.h"  /* private stuff */
#include "bvd_usb_ep0.h"

#undef DEBUG
//#define DEBUG 3
#if DEBUG
static unsigned int usb_debug = DEBUG;
#else
#define usb_debug 0     /* gcc will remove all the debug code for us */
#endif

enum { true = 1, false = 0 };
typedef int bool;
#ifndef MIN
#define MIN( a, b ) ((a)<(b)?(a):(b))
#endif

static EP0_state ep0_state = EP0_IDLE;

static int current_cfg_index = 0;
/***************************************************************************
  Prototypes
 ***************************************************************************/
/* "setup handlers" -- the main functions dispatched to by the
   .. isr. These represent the major "modes" of endpoint 0 operation */
void sh_setup_begin(void);    /* setup begin (idle) */
void sh_write( void );          /* writing data */
int  read_fifo( usb_dev_request_t * p );
void write_fifo( void );
void get_descriptor( usb_dev_request_t * pReq );
void queue_and_start_write( void * p, int req, int act );

/***************************************************************************
  Inline Helpers
 ***************************************************************************/

inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }

#if 0
/* print string descriptor */
static inline void psdesc( string_desc_t * p )
#else
inline void psdesc( string_desc_t * p )
#endif
{
    int i;
    int nchars = (p->bLength - 2) / sizeof(__u16);

    if(verbose_level > 0) printk("'");
    for (i = 0 ; i < nchars ; i++) {
        if(verbose_level > 0) printk("%c", (char)p->bString[i]);
    }
    if(verbose_level > 0) printk("'\n");
}

#if VERBOSITY
/* "pcs" == "print control status" */
inline void pcs(void)
{
    __u32 foo = UDCCSR0;

    printk( "%08x: %s %s %s %s %s %s\n",
            foo,
            foo & UDCCSR0_SA   ? "SA" : "",
            foo & UDCCSR0_OPC  ? "OPC" : "",
            foo & UDCCSR0_RNE  ? "RNE" : "",
            foo & UDCCSR0_SST  ? "SST" : "",
            foo & UDCCSR0_FST  ? "FST" : "",
            foo & UDCCSR0_DRWF ? "DRWF" : ""
          );
}
inline void preq(usb_dev_request_t * pReq)
{
    static char * tnames[] = { "dev", "intf", "ep", "oth" };
    static char * rnames[] = { "std", "class", "vendor", "???" };
    char * psz;

    switch (pReq->bRequest) {
        case GET_STATUS:        psz = "get stat"; break;
        case CLEAR_FEATURE:     psz = "clr feat"; break;
        case SET_FEATURE:       psz = "set feat"; break;
        case SET_ADDRESS:       psz = "set addr"; break;
        case GET_DESCRIPTOR:    psz = "get desc"; break;
        case SET_DESCRIPTOR:    psz = "set desc"; break;
        case GET_CONFIGURATION: psz = "get cfg";  break;
        case SET_CONFIGURATION: psz = "set cfg";  break;
        case GET_INTERFACE:     psz = "get intf"; break;
        case SET_INTERFACE:     psz = "set intf"; break;
        case SYNCH_FRAME:       psz = "synch frame"; break;
        default:                psz = "unknown";  break;
    }
    printk( "- [%s: %s req to %s. dir=%s]\n", psz,
            rnames[(pReq->bmRequestType >> 5) & 3],
            tnames[pReq->bmRequestType & 3],
            (pReq->bmRequestType & 0x80) ? "in" : "out");
}

#else
inline void pcs( void ){}
inline void preq( usb_dev_request_t *x){}
#endif

/***************************************************************************
  Globals
 ***************************************************************************/
static const char pszMe[] = "usbep0: ";

/* pointer to current setup handler */
void (*current_handler)(void) = sh_setup_begin;

/* global write struct to keep write
   ..state around across interrupts */
static struct {
    unsigned char *p;
    int bytes_left;
} wr;

/***************************************************************************
  Public Interface
 ***************************************************************************/

/* reset received from HUB (or controller just went nuts and reset by itself!)
   so udc core has been reset, track this state here  */

void ep0_reset(void)
{
#if 0
    if (usb_debug) ("%sep0_reset\n", pszMe);
#else
    if (usb_debug) printk("%sep0_reset\n", pszMe);
#endif
    /* reset state machine */
    current_handler = sh_setup_begin;
    wr.p = 0;
    wr.bytes_left = 0;
    usbd_info.address=0;
}

/* handle interrupt for endpoint zero */
void ep0_int_hndlr(void)
{
    if (usb_debug) printk("%sep0_int_hndlr\n", pszMe);
    pcs();
    (*current_handler)();
}

/***************************************************************************
  Setup Handlers
 ***************************************************************************/
/*
 * sh_setup_begin()
 * This setup handler is the "idle" state of endpoint zero. It looks for OPC
 * (OUT packet ready) to see if a setup request has been been received from the
 * host. 
 *
 */
void sh_setup_begin(void)
{
    usb_dev_request_t req;
    int request_type;
    int n;
    __u32 cs_reg_in = UDCCSR0;

    if (usb_debug) printk("%ssh_setup_begin\n", pszMe);

    /* Be sure out packet ready, otherwise something is wrong */
    if ((cs_reg_in & UDCCSR0_OPC) == 0) {
        /* we can get here early...if so, we'll int again in a moment  */
        if (usb_debug)
            printk("%ssetup begin: no OUT packet available. Exiting\n", pszMe);
        goto sh_sb_end;
    }

    if (((cs_reg_in & UDCCSR0_SA) == 0) && (ep0_state == EP0_IN_DATA_PHASE)) {
        if (usb_debug) printk("%ssetup begin: premature status\n", pszMe);

        /* premature status, reset tx fifo and go back to idle state*/
        UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF;

        ep0_state = EP0_IDLE;
        return;
    }

    if ((UDCCSR0 & UDCCSR0_RNE) == 0) {
        /* zero-length OUT? */
        if(verbose_level > 0) printk("%ssetup begin: zero-length OUT?\n", pszMe);
        goto sh_sb_end;
    }

    /* read the setup request */
    n = read_fifo(&req);
    if (n != sizeof(req)) {
        if (verbose_level > 0)
            printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
                " Stalling out...\n",
                pszMe, sizeof( req ), n );
        /* force stall, serviced out */
        UDCCSR0 = UDCCSR0_FST;
        goto sh_sb_end;
    }

    /* Is it a standard request? (not vendor or class request) */
    request_type = type_code_from_request( req.bmRequestType);
    if (request_type != 0) {
        if (verbose_level > 0)
            printk("%ssetup begin: unsupported bmRequestType: %d ignored\n",
                pszMe, request_type);
        goto sh_sb_end;
    }

#if 0
    {
        unsigned char * pdb = (unsigned char *) &req;
        if (usb_debug)
            printk("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
                pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
               );
        preq(&req);
    }
#endif

    /* Handle it */
    switch(req.bRequest) {

        case SET_ADDRESS:
            if (usb_debug) printk("%sSET_ADDRESS handled by UDC\n", pszMe);
            break;
#if 0 /* NOT_NEEDED */

        case SET_FEATURE:
            if (usb_debug) printk("%sSET_FEATURE handled by UDC\n", pszMe);
            break;

        case CLEAR_FEATURE:
            if (usb_debug) printk("%sCLEAR_FEATURE handled by UDC\n", pszMe);
            break;

        case GET_CONFIGURATION:
            if (usb_debug) printk("%sGET_CONFIGURATION handled by UDC\n", pszMe);
            break;

        case GET_STATUS:
            if (usb_debug) printk("%s%sGET_STATUS handled by UDC\n", pszMe);
            break;

        case GET_INTERFACE:
            if (usb_debug) printk("%sGET_INTERFACE handled by UDC\n", pszMe);
            break;

        case SYNCH_FRAME:
            if (usb_debug) printk("%sSYNCH_FRAME handled by UDC\n", pszMe);
            break;
#endif

        case GET_DESCRIPTOR:
            if (usb_debug) printk("%sGET_DESCRIPTOR\n", pszMe);
            get_descriptor(&req);
            break;

        case SET_INTERFACE:
            if (usb_debug) printk("%sSET_INTERFACE TODO...\n", pszMe);
            break;

        case SET_DESCRIPTOR:
            if (usb_debug) printk("%sSET_DESCRIPTOR TODO...\n", pszMe);
            break;

        case SET_CONFIGURATION:
            if (usb_debug) printk("%sSET_CONFIGURATION %d\n", pszMe, req.wValue);

/*
 * FIXME: Something is not quite right here... I only ever get a 
 * de-configure from the host. Ignoring it for now, since usb
 * ethernet won't do anything unless usb is 'configured'.
 *
 */
#if 0
            switch(req.wValue)
            {
                case 0:
                    /* configured */
                    usbctl_next_state_on_event(kEvConfig);
                    break;
                case 1:
                    /* de-configured */
                    usbctl_next_state_on_event(kEvDeConfig);
                    break;
                default:
                    if (usb_debug) printk("%sSET_CONFIGURATION: unknown configuration value (%d)\n", pszMe, req.wValue);
                    break;
            }
#endif
            break;
        default :
            if( verbose_level > 0 ) printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
            break;
    } /* switch(bRequest) */

sh_sb_end:
    return;
}

/*
 * sh_write()
 * 
 * Due to UDC bugs we push everything into the fifo in one go.
 * Using interrupts just didn't work right...
 * This should be ok, since control request are small.
 */
void sh_write()
{
    if (usb_debug) printk("sh_write\n");
    do {
        write_fifo();
    } while (ep0_state != EP0_END_XFER);
}

/***************************************************************************
  Other Private Subroutines
 ***************************************************************************/
/*
 * queue_and_start_write()
 * data == data to send
 * req == bytes host requested
 * act == bytes we actually have
 *
 * Sets up the global "wr"-ite structure and load the outbound FIFO 
 * with data.
 *
 */
void queue_and_start_write(void * data, int req, int act)
{
    if (usb_debug) printk("write start: bytes requested=%d actual=%d\n", req, act);

    wr.p = (unsigned char*) data;
    wr.bytes_left = MIN(act, req);

    ep0_state = EP0_IN_DATA_PHASE;
    sh_write();

    return;
}
/*
 * write_fifo()
 * Stick bytes in the endpoint zero FIFO.
 *
 */
void write_fifo(void)
{
    int bytes_this_time = MIN(wr.bytes_left, EP0_FIFO_SIZE);
    int bytes_written = 0;
    __u32 aligned_buf[EP0_FIFO_SIZE>>2];
    __u8  *data;

/* Nam9, 2004. 9. 5 */
    data = (__u8 *)aligned_buf;

    if (usb_debug) printk("%swr.p=0x%08x\n", __FUNCTION__, wr.p);

    if ((UDCCSR0 & UDCCSR0_IPR) == UDCCSR0_IPR) //last write not finished  yet
        return;
    memcpy(data, wr.p, bytes_this_time);

    /* 4-bytes word */
    while (bytes_this_time >= 4) {
        if (usb_debug) printk("%2.2X ", *wr.p);
        /* UDDR0 = *wr.p++; */
        UDCDR0 = *((__u32*)data);
        data += 4;
        bytes_written += 4;
        bytes_this_time -= 4;
    }

    while (bytes_this_time) {
        *((__u8*)&UDCDR0) = *data++;
        bytes_written ++;
        bytes_this_time --;
    }

    wr.p += bytes_written;
    wr.bytes_left -= bytes_written;

    usbd_info.stats.ep0_bytes_written += bytes_written;

    if ((wr.bytes_left == 0)) {
        wr.p = 0;      /* be anal */
        if (bytes_written == EP0_FIFO_SIZE) {//wait fifo write finish
            do {
            } while ((UDCCSR0 & UDCCSR0_IPR) == UDCCSR0_IPR);
        }
        //even bytes_written = EP0_FIFO_SIZE we must send a zero packet
        //if(bytes_written < EP0_FIFO_SIZE)
        {
            int count;
            int udccsr0;

            /* We always end the transfer with a short or zero length packet */
            ep0_state = EP0_END_XFER;
            current_handler = sh_setup_begin;

            /* Let the packet go... */
            UDCCSR0 |= UDCCSR0_IPR;

            /* Wait until we get to status-stage, then ack.
             *
             * When the UDC sets the UDCCSR0[OPC] bit, an interrupt
             * is supposed to be generated (see 12.5.1 step 14ff, PXA Dev Manual).   
             * That approach didn't work out. Usually a new SETUP command was
             * already in the fifo. I tried many approaches but was always losing 
             * at least some OPC interrupts. Thus the polling below...
             */
            count = 1000;
            udccsr0 = UDCCSR0;
            do {
                if ((UDCCSR0 & UDCCSR0_OPC)) {
                    /* clear OPC, generate ack */
                    UDCCSR0 |= UDCCSR0_OPC;
                    break;
                }
                count--;
            } while (count);

            if (usb_debug)
                printk("write fifo: count=%d UDCCSR0=%x UDCCSR0=%x\n", count, udccsr0, UDCCSR0);
        }
    }

    if (usb_debug)
        printk("write fifo: bytes sent=%d, bytes left=%d\n", bytes_written, wr.bytes_left);
}

/*
 * read_fifo()
 * Read bytes out of FIFO and put in request.
 * Called to do the initial read of setup requests
 * from the host. Return number of bytes read.
 *
 */
int read_fifo(usb_dev_request_t * request)
{
    int bytes_read = 0;
#if 0
    unsigned char * pOut = (unsigned char*) request;

    int udccsr0 = UDCCSR0;
#else
    unsigned char *pOut;
    int udccsr0;

    pOut = (unsigned char *)request;
    udccsr0 = UDCCSR0;

#endif

    if ((udccsr0 & SETUP_READY) == SETUP_READY) {
        /* ok it's a setup command */
        while (UDCCSR0 & UDCCSR0_RNE) {
            if (bytes_read >= sizeof(usb_dev_request_t)) {
                /* We've already read enought o fill usb_dev_request_t.
                 * Our tummy is full. Go barf... 
                 */
                if(verbose_level > 0) printk( "%sread_fifo(): read failure\n", pszMe );
                usbd_info.stats.ep0_fifo_read_failures++;
                break;
            }

            *((__u32*)pOut) = UDCDR0;
            pOut += 4;
            bytes_read +=4;
        }
    }
    if (usb_debug) printk("read_fifo %d bytes\n", bytes_read);

    /* clear SA & OPC */
    UDCCSR0 = SETUP_READY;

    usbd_info.stats.ep0_bytes_read += bytes_read;
    return bytes_read;
}

/*
 * get_descriptor()
 * Called from sh_setup_begin to handle data return
 * for a GET_DESCRIPTOR setup request.
 *
 * +-----+------------------------------------------------+-----------------+
 * | dev | cfg1 | intf 1 | ep 1..N | intf 2 | ep 1..N |...| cfg2 .......... |
 * +-----+------------------------------------------------+-----------------+
 */
void get_descriptor(usb_dev_request_t * pReq)
{
    string_desc_t * pString;
    ep_desc_t * pEndpoint = 0;
    config_desc_t *pcfg = 0;

    desc_t * pDesc = pxa_usb_get_descriptor_ptr();
    int type = pReq->wValue >> 8;
    int idx  = pReq->wValue & 0xFF;

    if (usb_debug) printk("%sget_descriptor for %d\n", pszMe, type);
    switch (type) {
        case USB_DESC_DEVICE:
            /* return device descritpor */
            queue_and_start_write(&pDesc->dev,
                    pReq->wLength,
                    pDesc->dev.bLength);
            break;

        // return config descriptor buffer, cfg, intf 1..N,  ep 1..N
        case USB_DESC_CONFIG: {
            int i,len;
            config_desc_t *cfg =(config_desc_t*)(pDesc->cdb);

            len=0;
            for (i = 0; i < pDesc->dev.bNumConfigurations; i++) {
                len += __le16_to_cpu(cfg->wTotalLength);
                cfg = (config_desc_t*)( (unsigned char*) cfg
                            + __le16_to_cpu(cfg->wTotalLength)) ;
            }

            queue_and_start_write(pDesc->cdb,
                        pReq->wLength,
                        len);
        }
        break;

        // not quite right, since doesn't do language code checking
        case USB_DESC_STRING:
            pString = pxa_usb_get_string_descriptor( idx);

            if (pString) {
                if (idx != 0) {  // if not language index
                    if(verbose_level > 0) printk("%sReturn string %d: ", pszMe, idx);
                    psdesc( pString);
                }
                queue_and_start_write(pString,
                        pReq->wLength,
                        pString->bLength);
            }
            else {
                if(verbose_level > 0) printk("%sunkown string index %d Stall.\n", pszMe, idx);
            }
            break;

        /*
        case USB_DESC_INTERFACE:
            for(i = 0; i < pDesc->intf_num; i++) {
                if (idx == pDesc->intf[i].bInterfaceNumber) {
                    queue_and_start_write(&pDesc->intf[i],
                            pReq->wLength,
                            pDesc->intf[i].bLength);
                }
            }
            break;

        case USB_DESC_ENDPOINT: 
            for(i = 0; i < pDesc->ep_num; i++) {
                if (idx == (0x0F & pDesc->ep[i].bEndpointAddress)) {
                    queue_and_start_write(&pDesc->ep[i],
                            pReq->wLength,
                            pDesc->ep[i].bLength);
                }
            }
            break;
            */

        default :
            if (verbose_level > 0)
                printk("%sunknown descriptor type %d. Stall.\n", pszMe, type);
            break;
    }
}

/* end usb_ep0.c - who needs this comment? */
